#include <QString>
#include <QTextCodec>
#include <algorithm>
#include <vector>

#include "INCLUDE/AnsiConverter.h"

static const auto pCodecANSI = QTextCodec::codecForName("Windows-1251");

int KMultiByteToWideChar(
        const unsigned int CodePage,
        const unsigned long dwFlags,
        const char *lpMultiByteStr,
        const int cbMultiByte,
        wchar_t *lpWideCharStr,
        const int cchWideChar
        )
{
    Q_UNUSED(CodePage);
    Q_UNUSED(dwFlags);
    Q_UNUSED(cbMultiByte);
    Q_UNUSED(cchWideChar);

    const auto str = toQtStr(lpMultiByteStr);

    str.toWCharArray(lpWideCharStr);

    return str.length();
}

int KWideCharToMultiByte(
        const unsigned int CodePage,
        const unsigned long dwFlags,
        const wchar_t *lpWideCharStr,
        const int cchWideChar,
        char *lpMultiByteStr,
        const int cbMultiByte,
        const char *lpDefaultChar,
        int *lpUsedDefaultChar
        )
{
    Q_UNUSED(CodePage);
    Q_UNUSED(dwFlags);
    Q_UNUSED(lpDefaultChar);
    Q_UNUSED(lpUsedDefaultChar);

    QString wideString = QString::fromWCharArray(lpWideCharStr, (cchWideChar < 0) ? -1 : cchWideChar);

    auto charArray = pCodecANSI->fromUnicode(wideString);

    const int outLenght = (cbMultiByte < 0) ? charArray.size() : (std::min)(cbMultiByte, charArray.size()) ;

    memcpy(lpMultiByteStr, charArray.constData(), outLenght * sizeof(lpMultiByteStr[0]));

    if (cbMultiByte < 0)
        lpMultiByteStr[charArray.size()]=0;
    else if (cbMultiByte > 0)
        lpMultiByteStr[(std::min)(cbMultiByte - 1,charArray.size())]=0;

    return charArray.size();
}

QString toQtStr(const wchar_t *pwString)
{
    return QString::fromWCharArray(pwString);
}

QString toQtStr(const char *pString, bool *isUtf8)
{
    auto str = QString::fromUtf8(pString);
    bool isAnsi = str.contains(QChar::ReplacementCharacter);
    if (isAnsi)
    {
        str = pCodecANSI->toUnicode(pString);
    }
    if (isUtf8 != nullptr)
        *isUtf8 = !isAnsi;

    return str;
}

void fromQtStr(const QString &string, char *pString, const ConvType convType )
{
    QByteArray charArray;
    switch (convType)
    {
    case ConvType::ToCp1251:
        charArray = pCodecANSI->fromUnicode(string);
        break;
    case ConvType::ToLocal8bit:
        charArray = string.toLocal8Bit();
        break;
    default:
        charArray = string.toUtf8();
        break;
    }

    memcpy(pString, charArray.constData(), charArray.length() * sizeof(pString[0]));

    pString[charArray.length()] = 0;
}

char* KCharUpper(char *lpsz)
{
    bool isUtf8 = false;
    auto str = toQtStr(lpsz, &isUtf8);

    fromQtStr(str.toUpper(), lpsz, isUtf8 ? ConvType::ToUtf8 : ConvType::ToCp1251);

    return lpsz;
}

wchar_t* KCharUpper(wchar_t *lpwsz)
{
    auto str = QString::fromWCharArray(lpwsz);
    str.toUpper().toWCharArray(lpwsz);

    return lpwsz;
}

unsigned long AnsiLowerBuff(char* lpsz, unsigned long cchLength)
{
    static thread_local std::vector<char> buf;

    buf.assign(cchLength + 1, 0);

    memcpy(buf.data(), lpsz, cchLength);

    bool isUtf8 = false;
    auto str = toQtStr(lpsz, &isUtf8);

    fromQtStr(str.toLower(), lpsz, isUtf8 ? ConvType::ToUtf8 : ConvType::ToCp1251);

    return str.length();
}
